package com.mxpio.erp.inventory.service.impl;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.mxpio.erp.common.LotStatusUtil;
import com.mxpio.erp.common.inventory.entity.VInvLotStock;
import com.mxpioframework.jpa.query.Criteria;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mxpio.erp.common.enums.CommonEnums;
import com.mxpio.erp.common.enums.ItemEnums;
import com.mxpio.erp.common.inventory.entity.InvFlow;
import com.mxpio.erp.common.inventory.entity.Lot;
import com.mxpio.erp.common.inventory.entity.WarehouseOrder;
import com.mxpio.erp.common.inventory.service.LotService;
import com.mxpio.erp.common.jpa.entity.BizNoticeType;
import com.mxpio.erp.common.jpa.model.Notice;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.quality.entity.InQualityOrder;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.technology.entity.ItemPropKey;
import com.mxpio.erp.common.technology.entity.ItemStoreProp;
import com.mxpio.erp.common.vo.InvFlowSummaryVo;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.common.workshop.entity.ManufactureOrder;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.lin.Linq;
import com.mxpioframework.system.service.SnRuleService;
import com.mxpioframework.system.service.impl.BaseServiceImpl;

@Service
public class LotServiceImpl extends BaseServiceImpl<Lot> implements LotService {
	
	@Autowired
	private SnRuleService snRuleService;
	
	@Override
	@Transactional(readOnly = false)
	public Lot getNewLot(Order order, OrderExcuteLineVo vo) {
		ItemPropKey key = new ItemPropKey();
		key.setCode(vo.getItemCode());
		key.setPropType(ItemEnums.PropType.ITEM.getTypeCode());
		ItemStoreProp prop = JpaUtil.getOne(ItemStoreProp.class, key);
		if(CommonEnums.YesNo.YES.getCode().equals(prop.getBatchControl())) {
			Lot lot = new Lot();
			String lotNoEx = "";
			if(order instanceof WarehouseOrder) {
				lotNoEx = ((WarehouseOrder) order).getOriginBizNo() + "-" + vo.getBizOrderlineNo() + "####";
			}else if(order instanceof ManufactureOrder){
				lotNoEx = order.getBizNo() + "-" + "####";
			}
			lot.setLotNo(snRuleService.execute(lotNoEx, null).toString());
			lot.setLotStatus(LotStatusUtil.toBinaryString("0"));
			lot.setSubLotIndex("0");
			JpaUtil.save(lot);
			return lot;
		}else {
			return null;
		}
	}
	
	@Override
	@Transactional(readOnly = false)
	public Lot getNewLot(Notice notice, OrderExcuteLineVo vo, Map<String, BizNoticeType> typesMap) {
		
		ItemPropKey key = new ItemPropKey();
		key.setCode(vo.getItemCode());
		key.setPropType(ItemEnums.PropType.ITEM.getTypeCode());
		ItemStoreProp prop = JpaUtil.getOne(ItemStoreProp.class, key);
		if(CommonEnums.YesNo.YES.getCode().equals(prop==null?CommonEnums.YesNo.YES.getCode():prop.getBatchControl())) {
			Lot lot = new Lot();
			String lotNo = null;
			if(notice instanceof InQualityOrder){
				lotNo = notice.getBizOrderNo()+ "-" + vo.getNoticeLineNo();
			}else{
				BizNoticeType bizNoticeType = typesMap.get((new InQualityOrder()).getTypeCode());
				StringBuffer expression = new StringBuffer(bizNoticeType.getTypePrefix());
				for(int i=0 ;i < bizNoticeType.getTypeSize(); i++){
					expression.append("#");
				}
				//lotNo = snRuleService.execute(expression.toString(), null).toString()+ "-" + vo.getNoticeLineNo();
				if (StringUtils.isBlank(notice.getTempInQualityOrderNo())){
					notice.setTempInQualityOrderNo(snRuleService.execute(expression.toString(), null).toString());
				}
				lotNo = notice.getTempInQualityOrderNo()+ "-" + vo.getNoticeLineNo();//弃用上边注释部分代码的原因是：生成的批次号不规范（Q0100000001-1,Q0100000002-2而非Q0100000001-1，Q0100000001-2）
			}
			lot.setLotNo(lotNo);
			lot.setLotStatus(LotStatusUtil.toBinaryString("0"));
			lot.setSubLotIndex("0");
			JpaUtil.save(lot);
			return lot;
		}else {
			return null;
		}
	}

	@Override
	@Transactional(readOnly = true)
	public BigDecimal getStock(String whCode, String itemCode, String lotNo,String subLotIndex,boolean excludeHold) {//excludeHold是否排除锁定
		String subWhereSql = "";
		if (excludeHold){
			subWhereSql += " and exists (select lot_no_ from mb_erp_inv_lot t where t.lot_no_=f.lot_no_ and t.sub_lot_index_ = f.sub_lot_index_ and t.lot_status_='0000000000000000' ) ";
		}
		String sqlIn = "select sum(f.quantity_) as quantity_ from mb_erp_inv_flow f where f.access_type_ = 'in' AND f.lot_no_ = '" + lotNo+"' and f.sub_lot_index_='"+subLotIndex
				+ "' AND f.wh_code_ = '" + whCode + "' AND f.item_code_ = '" + itemCode + "'" +subWhereSql;
		BigDecimal sumIn = (BigDecimal) JpaUtil.nativeQuery(sqlIn).getSingleResult();
		String sqlOut = "select sum(f.quantity_) as quantity_ from mb_erp_inv_flow f where f.access_type_ = 'out' AND f.lot_no_ = '" + lotNo +"' and f.sub_lot_index_='"+subLotIndex
				+ "' AND f.wh_code_ = '" + whCode + "' AND f.item_code_ = '" + itemCode + "'" +subWhereSql;;
		BigDecimal sumOut = (BigDecimal) JpaUtil.nativeQuery(sqlOut).getSingleResult();
		return (sumIn==null?BigDecimal.ZERO:sumIn).subtract(sumOut==null?BigDecimal.ZERO:sumOut);
	}

	@Override
	@Transactional(readOnly = true)
	public BigDecimal getMaxQuantityByWo(String woBizNo, String woBizLineNo) {
		String sumSql = "select sum(quantity_) as quantity_ from mb_erp_inv_flow where wo_biz_no_ = '" + woBizNo + "' AND wo_biz_line_no_ = '" + woBizLineNo + "'";
		BigDecimal sum = (BigDecimal) JpaUtil.nativeQuery(sumSql).getSingleResult();
		return (sum==null?BigDecimal.ZERO:sum);
	}

	@Override
	@Transactional(readOnly = true)
	public Collection<InvFlowSummaryVo> getExecutedInvFlows(String bizNo, String lineNo, String accessType, String whCode) {
		Linq lin = JpaUtil.linq(InvFlow.class)
				.collect("itemCode", Item.class, "itemCode")
				.equal("originBizNo", bizNo)
				.equal("originBizLineNo", lineNo);

		if(StringUtils.isNotBlank(whCode)){
			lin = lin.equal("whCode", whCode);
		}
		//增加排序字段
		Criteria criteria = Criteria.create();
		criteria.addOrder(new com.mxpioframework.jpa.query.Order("lotNo",false))
				.addOrder(new com.mxpioframework.jpa.query.Order("subLotIndex",false));
		List<InvFlow> flows = lin.where(criteria).list();
				
		Map<String, InvFlowSummaryVo> summaryMap = new HashMap<>();
		Map<String, InvFlowSummaryVo> oppoSummaryMap = new HashMap<>();
		for(InvFlow flow : flows){
			String key;
			if(StringUtils.isBlank(whCode)){
				key = flow.getLotNo()+flow.getSubLotIndex();
			}else{
				key = flow.getLotNo()+flow.getSubLotIndex()+flow.getWhCode();
			}
			InvFlowSummaryVo vo = summaryMap.get(key);
			
			if(vo == null){
				vo = new InvFlowSummaryVo();
				vo.setItemCode(flow.getItemCode());
				if(flow.getItem() != null){
					vo.setItemName(flow.getItem().getItemName());
					vo.setItemSpec(flow.getItem().getItemSpec());
					vo.setDrawingNo(flow.getItem().getDrawingNo());
					vo.setUnitCode(flow.getItem().getUnitCode());
				}
				vo.setLotNo(flow.getLotNo());
				vo.setSubLotIndex(flow.getSubLotIndex());
				if(StringUtils.isNotBlank(whCode)){
					vo.setWhCode(whCode);
				}
				
				vo.setQuantity(BigDecimal.ZERO);
			}
			if(accessType.equals(flow.getAccessType())){
				// 调拨时写入仓库
				if(StringUtils.isBlank(whCode) && vo.getWhCode() == null){
					vo.setWhCode(flow.getWhCode());
				}
				vo.setQuantity(vo.getQuantity().add(flow.getQuantity()));
			}else{
				vo.setQuantity(vo.getQuantity().subtract(flow.getQuantity()));
			}
			if(BigDecimal.ZERO.compareTo(vo.getQuantity()) == 0){
				summaryMap.remove(key);
			}if(BigDecimal.ZERO.compareTo(vo.getQuantity()) > 0){//为负的情况
				vo.setQuantity(vo.getQuantity().abs());//取绝对值，方便计算
				oppoSummaryMap.put(key, vo);
			}else{
				summaryMap.put(key, vo);
			}
		}
		/*
			以上的方法逻辑中，如果遇到不良退料通知单执行，有拆单情况产生后，会产生不合格品仓且批索引加一且状态为锁定的批次库存。这部分数据由正常批拆分而来，这部分数据需要计算到原批次中。
		dsf*/
		if (!oppoSummaryMap.isEmpty()){
			Map<String, List<InvFlowSummaryVo>> voMaps = summaryMap.values().stream().collect(Collectors.groupingBy(InvFlowSummaryVo::getLotNo));
			Map<String, List<InvFlowSummaryVo>> opVoMaps = oppoSummaryMap.values().stream().collect(Collectors.groupingBy(InvFlowSummaryVo::getLotNo));
			for (String lotNo:voMaps.keySet()){
				if (opVoMaps.containsKey(lotNo)){
					for (InvFlowSummaryVo vo:voMaps.get(lotNo)){
						if (vo.getQuantity().compareTo(BigDecimal.ZERO)==0){
							continue;
						}
						for (InvFlowSummaryVo oppoVo:opVoMaps.get(lotNo)){
							BigDecimal voQuantity = vo.getQuantity();
							BigDecimal oppoVoQuantity = oppoVo.getQuantity();
							if (oppoVoQuantity.compareTo(BigDecimal.ZERO)==0){
								continue;
							}
							if (voQuantity.compareTo(oppoVoQuantity)>=0){
								oppoVo.setQuantity(BigDecimal.ZERO);
								vo.setQuantity(voQuantity.subtract(oppoVoQuantity));
							}else{
								oppoVo.setQuantity(oppoVoQuantity.subtract(voQuantity));
								vo.setQuantity(BigDecimal.ZERO);
							}
						}
					}
				}
			}
		}
		/*Iterator<String> iterator = summaryMap.keySet().iterator();
		while (iterator.hasNext()){
			String key = iterator.next();
			InvFlowSummaryVo vo = summaryMap.get(key);
			if(BigDecimal.ZERO.compareTo(vo.getQuantity()) == 0){
				iterator.remove();
			}
		}*/
		summaryMap.keySet().removeIf(key -> summaryMap.get(key).getQuantity().compareTo(BigDecimal.ZERO) == 0);
		return summaryMap.values();
	}
	@Override
	@Transactional(readOnly = true)
	@Deprecated
	public BigDecimal getItemLiveStock(String whCode, String itemCode) {//查询实时库存 废弃
		String sqlIn = "select sum(quantity_) as quantity_ from mb_erp_inv_flow where access_type_ = 'in' " +
				" AND wh_code_ = '" + whCode + "' AND item_code_ = '" + itemCode + "'";
		BigDecimal sumIn = (BigDecimal) JpaUtil.nativeQuery(sqlIn).getSingleResult();
		String sqlOut = "select sum(quantity_) as quantity_ from mb_erp_inv_flow where access_type_ = 'out' " +
				" AND wh_code_ = '" + whCode + "' AND item_code_ = '" + itemCode + "'";
		BigDecimal sumOut = (BigDecimal) JpaUtil.nativeQuery(sqlOut).getSingleResult();
		return (sumIn==null?BigDecimal.ZERO:sumIn).subtract(sumOut==null?BigDecimal.ZERO:sumOut);
	}
	@Override
	@Transactional(readOnly = true)
	public BigDecimal getItemLiveStockWithLotStatus(String whCode, String itemCode,boolean excludeHold) {//查询实时库存 并过滤状态
		String subWhereSql = "";
		if (excludeHold){
			subWhereSql += " and exists (select lot_no_ from mb_erp_inv_lot t where t.lot_no_=f.lot_no_ and t.sub_lot_index_ = f.sub_lot_index_ and t.lot_status_='0000000000000000' ) ";
		}
		String sqlIn = "select sum(f.quantity_) as quantity_ from mb_erp_inv_flow f where f.access_type_ = 'in' AND f.wh_code_ = '" + whCode + "' AND f.item_code_ = '" + itemCode + "'" +subWhereSql;
		BigDecimal sumIn = (BigDecimal) JpaUtil.nativeQuery(sqlIn).getSingleResult();
		String sqlOut = "select sum(f.quantity_) as quantity_ from mb_erp_inv_flow f where f.access_type_ = 'out' AND f.wh_code_ = '" + whCode + "' AND f.item_code_ = '" + itemCode + "'" +subWhereSql;;
		BigDecimal sumOut = (BigDecimal) JpaUtil.nativeQuery(sqlOut).getSingleResult();
		return (sumIn==null?BigDecimal.ZERO:sumIn).subtract(sumOut==null?BigDecimal.ZERO:sumOut);
	}

	@Override
	@Transactional(readOnly = true)
	public synchronized Integer getCountByLotNo(String lotNo) {//获取批次已插入条数，来得到待插入同lotno数据的index值
		/*String sqlIn = "select count(1) from mb_erp_inv_lot where lot_no_='"+lotNo+"'";
		BigInteger cnt = (BigInteger) JpaUtil.nativeQuery(sqlIn).getSingleResult();*/
		Long count = JpaUtil.linq(Lot.class).equal("lotNo", lotNo).count();
		return count.intValue();
	}
	@Override
	@Transactional(readOnly = false)
	public Lot splitNewLot(String lotNo, String originSubLotIndex, Integer holdIndex){//拆批
		Lot lot = new Lot();
		lot.setLotNo(lotNo);
		lot.setSubLotIndex(this.getCountByLotNo(lotNo).toString());//索引
		String binaryString = LotStatusUtil.toBinaryString("0");//默认正常
		if (holdIndex!=null){
			lot.setLotStatus(LotStatusUtil.switchBinaryByIndex(binaryString, holdIndex));
		}else{
			lot.setLotStatus(binaryString);
		}
		//记录源批
		lot.setOriginSubLotIndex(originSubLotIndex);
		JpaUtil.save(lot);
		return lot;
	}
}
